﻿#region LICENSE
// ------------------------------------------------------------------------------------------------------------------------------------
//    Project: https://code.google.com/p/comlib/
//    File: Helper.cs
//    Copyright (C) 2003 Sergy Laman and other authors

//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.

//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.

//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
// ------------------------------------------------------------------------------------------------------------------------------------
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Xsl;

namespace QickSoft.Common
{
    public static class Helper
    {
        /// <summary>
        /// Replicates string n times
        /// </summary>
        /// <param name="what"></param>
        /// <param name="times"></param>
        /// <returns></returns>
        public static string Replicate(string what, int times)
        {
            string s = "";
            for (int i = 0; i < times; i++)
            {
                s += what;
            }
            return s;
        }

        public static object Trim(object o)
        {
            if (o == null) return null;
            Type t = o.GetType();
            foreach (PropertyInfo pi in t.GetProperties())
            {
                if (pi.PropertyType == Type.GetType("System.String") )
                {
                    object val = pi.GetValue(o, null);
                    if (val == null || val == DBNull.Value) continue;
                    pi.SetValue(o, Cast.ToString(val), null);
                }
            }
            return o;
        }

        public static object GetProperty(object o, string property)
        {
            if (o == null) return null;
            Type t = o.GetType();
            PropertyInfo pi = t.GetProperty(property);
            if (pi == null) return null;
            return pi.GetValue(o, null);
        }

        /// <summary>
        /// Drops empty nodes from dataset passed
        /// </summary>
        /// <param name="xml"></param>
        public static string TrimXml(string xml)
        {
            //search for dates without seconds add 00 to them
            xml = Regex.Replace(xml, @"(\d{4}-\d{2}-\d{2}T\d{2}:\d{2})([^:])", "$1:00$2");
            //strip empty nodes
            string xslt = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">    <xsl:output omit-xml-declaration=\"no\" indent=\"no\"/>    <xsl:strip-space elements=\"*\" />    <xsl:template match=\"*[not(node()) and not(./@*)]\"/>    <xsl:template match=\"@* | node()\">        <xsl:copy>            <xsl:apply-templates select=\"@* | node()\"/>        </xsl:copy>    </xsl:template></xsl:stylesheet>";
            XmlDocument xsl = new XmlDocument();
            xsl.LoadXml(xslt);
            StringWriter writer = new StringWriter();
            XslCompiledTransform transform = new XslCompiledTransform();
            transform.Load(xsl);
            transform.Transform(new XmlTextReader(new StringReader(xml)), null, writer);
            writer.Flush();
            return writer.ToString();
        }
        /// <summary>
        /// Evaluate DataSet expression
        /// </summary>
        /// <param name="drChanged"></param>
        /// <param name="p"></param>
        /// <returns></returns>
        public static object Evaluate(DataRow dr, string expression)
        {
            expression = RoundEvaluate(expression, dr);
            DataTable dt = dr.Table;
            dt.Columns.Add(new DataColumn("test_expression", typeof(object), expression));
            object exp = dr["test_expression"];
            dt.Columns.Remove("test_expression");
            return exp;
        }
        /// <summary>
        /// Evaluates function ROUND round value to 2 decimal places
        /// </summary>
        /// <param name="expression"></param>
        /// <param name="drChanged"></param>
        /// <returns></returns>
        private static string RoundEvaluate(string expression, DataRow drChanged)
        {
            ArrayList l = new ArrayList();
            Regex r = new Regex(@"ROUND\(([^\(\)]+)\){1}");
            DataTable dt = drChanged.Table;
            for (Match m = r.Match(expression); m.Success; m = m.NextMatch())
            {
                dt.Columns.Add("xtest", typeof(double), m.Groups[1].ToString());
                double result = Cast.ToDouble(dt.Rows[0]["xtest"]);
                dt.Columns.Remove("xtest");
                result = Math.Round(result, 2);
                l.Add(new string[2] { m.Value, result.ToString() });
            }
            foreach (string[] e in l)
            {
                expression = expression.Replace(e[0], e[1]);
            }
            return expression;
        }

        #region EXTENSION METHODS
        public static new bool Equals(object obj, object obj1)
        {
            return string.Equals(Cast.ToString(obj), Cast.ToString(obj1), StringComparison.OrdinalIgnoreCase);
        }

        public static bool In(this object o, params object[] values)
        {
            string oo = Cast.ToString(o);
            foreach (object v in values) if (string.Equals(oo, Cast.ToString(v), StringComparison.OrdinalIgnoreCase)) return true;
            return false;
        }
        public static bool Like(this object o, params object[] values)
        {
            string oo = Cast.ToString(o);
            foreach (object v in values) if (oo.ToUpper().StartsWith(Cast.ToString(v).ToUpper())) return true;
            return false;
        }
        public static bool In(string what, params string[] values)
        {
            foreach (string s in values) if (what.Trim().ToLower() == s.ToLower().Trim()) return true;
            return false;
        }

        public static bool In(string what, params char[] values)
        {
            foreach (char s in values) if (what.Trim().ToLower() == s.ToString().ToLower().Trim()) return true;
            return false;
        }

        public static bool In(object what, params string[] values)
        {
            return Helper.In(Cast.ToString(what), values);
        }
        #endregion

        /// <summary>    
        /// Replaces the first occurrence of a string found within     
        /// another string.    
        /// </summary>    
        /// <param name="oldValue">A System.String to be replaced.</param>    
        /// <param name="newValue">A System.String to replace the first occurrence of oldValue.</param>    
        public static string ReplaceFirstOccurrence(string original, string oldValue, string newValue)
        {
            int loc = original.IndexOf(oldValue); return original.Remove(loc, oldValue.Length).Insert(loc, newValue);
        }
        public static string ReplaceLastOccurrence(string original, string oldValue, string newValue)
        {
            int loc = original.LastIndexOf(oldValue); return original.Remove(loc, oldValue.Length).Insert(loc, newValue);
        }

        public static string CutNode(string xml, string node)
        {
            string xml1 = xml.ToLower();
            int startPos = 0, len = 0;
            startPos = xml1.IndexOf("<" + node.ToLower() + ">") + ("<" + node.ToLower() + ">").Length;
            len = xml1.IndexOf("</" + node.ToLower() + ">") - startPos;
            return xml.Substring(startPos, len);
        }
        /// <summary>
        /// Round hours to quarters
        /// Assume they pass hours.0-99 minutes
        /// </summary>
        public static double RoundHours(double hours, bool up)
        {
            hours = Math.Round(hours, 2);

            int intHours = (int)hours;
            int minutes = (int)(((double)hours - (double)intHours) * 100);
            int wholeQuarters = minutes / 25;
            double reminder = minutes % 25;

            if (reminder > 0 && up) wholeQuarters += 1;

            return intHours + (wholeQuarters * 25) / 100.0;
        }

        static public object EnumParse<T>(string value)
        {
	    if (!Enum.IsDefined(typeof(T), value)) return null;
            return (T)Enum.Parse(typeof(T), value);
        }

    }

    public static class IEnumerableExtensions
    {
        /// <summary> 
        /// Invokes a transform function on each element of a sequence and returns the minimum Double value  
        /// if the sequence is not empty; otherwise returns the specified default value.  
        /// </summary> 
        /// <typeparam name="TSource">The type of the elements of source.</typeparam> 
        /// <param name="source">A sequence of values to determine the minimum value of.</param> 
        /// <param name="selector">A transform function to apply to each element.</param> 
        /// <param name="defaultValue">The default value.</param> 
        /// <returns>The minimum value in the sequence or default value if sequence is empty.</returns> 
        public static double MinOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, double> selector, double defaultValue)
        {
            if (source.Any<TSource>())
                return source.Min<TSource>(selector);

            return defaultValue;
        }

        /// <summary> 
        /// Invokes a transform function on each element of a sequence and returns the maximum Double value  
        /// if the sequence is not empty; otherwise returns the specified default value.  
        /// </summary> 
        /// <typeparam name="TSource">The type of the elements of source.</typeparam> 
        /// <param name="source">A sequence of values to determine the maximum value of.</param> 
        /// <param name="selector">A transform function to apply to each element.</param> 
        /// <param name="defaultValue">The default value.</param> 
        /// <returns>The maximum value in the sequence or default value if sequence is empty.</returns> 
        public static double MaxOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, double> selector, double defaultValue)
        {
            if (source.Any<TSource>())
                return source.Max<TSource>(selector);

            return defaultValue;
        }        

    }
}



